import csv
import io
import uuid
from threading import Thread

import phonenumbers
from django.core.mail import EmailMultiAlternatives
from django.db import transaction
from django.forms import model_to_dict
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django_tenants.utils import schema_context
from phonenumber_field.validators import validate_international_phonenumber
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import Client, ClientGuardian, ClientGuardianAddress, ClientAddress, ClientInsurance, \
    ClientAuthorizationTracking, ClientServices
from apps.users.permissions import IsProvider, IsClient
from ..booking.models import Appointment, Booking
from ..booking.types import AppointmentStatusChoices, BookingStatusChoices
from ..core.pagination import paginated_queryset_response
from datetime import datetime
from rest_framework import status
from django.db.models import OuterRef, Subquery, Q, DateTimeField

from ..tenant.models import ProviderServices
from ..users.models import User, ClientSchemaHandle, Provider, ProviderLicense
from apps.core.utils.gcs_operations import generate_assigned_url, upload_file
from ..users.views import send_email_confirm_account
from django.conf import settings


def send_csv_email(user, csv_buffer):

    from_email = settings.CONTACT_EMAIL
    to = user['email']
    subject = 'Your CSV Data'
    body = 'Please find attached your CSV data.'

    # Create the email message
    msg = EmailMultiAlternatives(subject=subject, body=body, from_email=from_email, to=[to])

    # Attach the CSV file to the email
    msg.attach('data.csv', csv_buffer.getvalue(), 'text/csv')

    try:
        msg.send()
    except Exception as ex:
        print(ex)


def client_details_thread(user, client):
    if user.is_provider:
        with schema_context(user.provider.schema_name):
            client_data = {
                'id': client.id,
                'first_name': client.first_name,
                'middle_name': client.middle_name,
                'last_name': client.last_name,
                'status': client.status,
                'date_of_birth': client.date_of_birth,
                'phone_number': client.phone_number,
                'email': client.email,
                'billing_type': client.billing_type,
                'suffix': client.suffix,
                'messaging': client.messaging,
                'preferred_name': client.preferred_name,
                'is_minor': client.is_minor,
                'referred_by': client.referred_by,
                'relationship_status': client.relationship_status,
                'employment_status': client.employment_status,
                'credit_card': client.credit_card,
                'ethnicity': client.ethnicity,
                'preferred_language': client.preferred_language,
                'gender': client.gender,
                'races': client.races,
                'appointment_email_reminder': client.appointment_email_reminder,
                'billing_email_reminder': client.billing_email_reminder,
                'note': client.note,
                'gender_identity': client.gender_identity,
                'date_started': client.date_started,
                'client_guardian': list(client.client_guardian.values()) or None,
                'client_address': list(client.client_address.values()) or None,
                'client_insurance': list(client.client_insurance.values()) or None,
                'client_authorization': list(client.client_authorization.values()) or None,
                'client_service': list(client.client_service.values()) or None,
                'client_appointment': list(client.client_appointment.values()) or None

            }
            response = HttpResponse(content_type='text/csv')
            response['Content-Disposition'] = 'attachment; filename="data.csv"'

            csv_data = io.StringIO()

            # Write data to CSV file
            csv_writer = csv.DictWriter(csv_data,
                                        fieldnames=['id', 'first_name', 'middle_name', 'is_minor', 'employment_status',
                                                    'note',
                                                    'races', 'phone_number', 'client_guardian', 'client_insurance',
                                                    'date_of_birth', 'date_started', 'relationship_status',
                                                    'preferred_language', 'billing_type', 'client_authorization',
                                                    'ethnicity',
                                                    'billing_email_reminder', 'email', 'suffix', 'credit_card',
                                                    'last_name',
                                                    'messaging', 'client_service', 'preferred_name', 'user_provider',
                                                    'gender',
                                                    'client_address', 'gender_identity', 'referred_by',
                                                    'appointment_email_reminder', 'status', 'client_appointment'])
            csv_writer.writeheader()
            csv_writer.writerow(client_data)

            # csv_filename = 'data.csv'
            # with open(csv_filename, 'w', newline='') as csvfile:
            #     csvfile.write(csv_data.getvalue())
            send_csv_email(user, csv_data)


def save_client_addresses(client, addresses):
    for address in addresses:
        if 'client' in address:
            address.pop('client')
        ClientAddress.objects.create(client=client, **address)


def save_client_guardian_addresses(client, addresses):
    ClientGuardianAddress.objects.create(guardian=client, **addresses)
    # instances_to_create = [ClientGuardianAddress(guardian=client, **address_data) for address_data in addresses]
    # ClientGuardianAddress.objects.bulk_create(instances_to_create)


# Create your views here
def save_client_guardian(client, client_guardian):
    for guardian in client_guardian:
        guardian_addresses = None
        if 'guardian_addresses' in guardian and guardian['guardian_addresses'] is not None:
            guardian_addresses = guardian.pop('guardian_addresses')
        if 'date_of_birth' in guardian and guardian['date_of_birth'] is not None:
            guardian['date_of_birth'] = datetime.fromtimestamp(int(guardian['date_of_birth']))
        if 'phone_number' in guardian:
            phone_number = guardian['phone_number']
            try:
                parsed_phone_number = phonenumbers.parse(phone_number, "US")

                # Check if the parsed phone number is valid
                if phonenumbers.is_valid_number(parsed_phone_number):
                    print("Phone number is valid:",
                          phonenumbers.format_number(parsed_phone_number, phonenumbers.PhoneNumberFormat.E164))
                else:
                    raise ValidationError("Phone number is invalid")
            except phonenumbers.phonenumberutil.NumberParseException:
                raise ValidationError("Invalid phone number format")
        email = guardian['email']

        if User.objects.filter(email=email).count() > 0:
            raise ValidationError("Email already exist")
        guardian['password'] = User.objects.make_random_password()
        user_guardian = ClientGuardian.objects.create(user_guardian=client, **guardian)
        if guardian_addresses:
            save_client_guardian_addresses(user_guardian, guardian_addresses)


def save_client_insurance(client, insurance):
    ClientInsurance.objects.create(client=client, **insurance)


def update_client_insurance(client, card, insurance):
    insurance.pop('superBillsPayment')
    if insurance['coinsurance'] == '':
        insurance['coinsurance'] = 0.0

    if insurance['insurance_type']:
        insurance['insurance_type'] = str(insurance['insurance_type'])

    if insurance['primary_insured']:
        insurance['primary_insured'] = str(insurance['primary_insured'])

    if insurance['deductible'] == '':
        insurance['deductible'] = 0.0
    ClientInsurance.objects.create(client=client, card=card, **insurance)


def save_client_authorization_tracking(client, data):
    if 'validity' in data:
        data['validity'] = datetime.fromtimestamp(int(data['validity']))

    ClientAuthorizationTracking.objects.create(client=client, **data)


def save_client_services(client, data):
    for service in data:
        provider_service = ProviderServices.objects.filter(id=service['service']).first()
        if provider_service:
            service['service'] = provider_service
            ClientServices.objects.create(client=client, **service)
        else:
            raise ValidationError('Provider Service does not exist')


@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def client_api(request):
    if request.method == 'POST':
        data = request.data
        if 'date_of_birth' in data and data['date_of_birth'] is not None:
            data['date_of_birth'] = datetime.fromtimestamp(int(data['date_of_birth']))

        if 'date_started' in data and data['date_started'] is not None:
            data['date_started'] = datetime.fromtimestamp(int(data['date_started']))

        phone_number = data.get('phone_number')

        try:
            parsed_phone_number = phonenumbers.parse(phone_number, "US")

            if phonenumbers.is_valid_number(parsed_phone_number):
                print("Phone number is valid:",
                      phonenumbers.format_number(parsed_phone_number, phonenumbers.PhoneNumberFormat.E164))
            else:
                raise ValidationError("Phone number is invalid")
        except phonenumbers.phonenumberutil.NumberParseException:
            raise ValidationError("Invalid phone number format")

        email = data['email']
        if User.objects.filter(email=email).count() > 0:
            raise ValidationError("Email already exist")
        if 'password' not in data:
            data['password'] = User.objects.make_random_password()

        client_guardian = None
        addresses = None
        insurance = None
        client_services = None
        authorization_tracking = None
        if 'client_guardian' in data and data['client_guardian'] is not None:
            client_guardian = data.pop('client_guardian')
        if 'addresses' in data and data['addresses'] is not None:
            addresses = data.pop('addresses')
        if 'client_insurance' in data and data['client_insurance'] is not None:
            insurance = data.pop('client_insurance')
        if 'authorization_tracking' in data and data['authorization_tracking'] is not None:
            authorization_tracking = data.pop('authorization_tracking')
        if 'services' in data and data['services'] is not None:
            client_services = data.pop('services')

        user_id = request.user.provider.id
        client = Client.objects.create_user(**data)
        client.is_client = True
        client.user_provider = user_id

        client.save()
        with schema_context("public"):
            provider = request.user.provider
            ClientSchemaHandle.objects.create(provider=provider, schema_name=provider.schema_name, client_id=client.id)

        if client_guardian:
            save_client_guardian(client, client_guardian)
        if addresses:
            save_client_addresses(client, addresses)
        if insurance:
            save_client_insurance(client, insurance)
        if authorization_tracking:
            save_client_authorization_tracking(client, authorization_tracking)
        if client_services:
            save_client_services(client, client_services)

        thread = Thread(target=send_email_confirm_account, args=[client, 'CLIENT', request.user])
        thread.start()

        return Response({'id': client.id}, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-date_started')
        current_datetime = datetime.now()
        offset = int(request.GET.get('offset', 0))
        limit = int(request.GET.get('limit', 10))
        count = Client.objects.all().count()
        query = Client.objects.all().order_by(ordering)
        get_search = request.GET.get('search')
        if get_search:
            query = query.filter(
                Q(email__icontains=get_search) |
                Q(first_name__icontains=get_search) |
                Q(last_name__icontains=get_search)
            )
        get_status = request.GET.get('status')
        if get_status:
            query = query.filter(status=int(get_status))
        query = query[offset: offset + limit]
        clients = list()
        for q in query:
            client = model_to_dict(q,
                                   exclude=['password', 'groups', 'user_permissions'])

            appointments = Appointment.objects.filter(
                Q(clients=q.id, start_date__date__lte=current_datetime.date()) |
                Q(booking__start_date__date__lte=current_datetime.date(), clients=q.id)
            ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by('-start_date').values('start_date',
                                                                                                      'booking__start_date').first()
            if appointments:
                if appointments['start_date']:
                    client['latest_appointment'] = appointments.get('start_date').timestamp()
                elif appointments['booking__start_date']:
                    client['latest_appointment'] = appointments.get('booking__start_date').timestamp()
                else:
                    client['latest_appointment'] = None
            else:
                client['latest_appointment'] = None
            client['id'] = q.id
            get_avatar = generate_assigned_url(client['avatar']) if client['avatar'] else ''
            if get_avatar:
                client['avatar'] = get_avatar
            else:
                client['avatar'] = ''

            # latest_appointment_date_unix = str(int(
            #     q.latest_appointment_date.timestamp())) if q.latest_appointment_date else None
            # client['latest_appointment'] = latest_appointment_date_unix

            clients.append(client)
        return Response({'offset': offset, 'limit': limit, 'count': count, 'result': clients})


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsClient])
@transaction.atomic
def client_me_api(request):
    client = request.user.client
    client_guardian_queryset = ClientGuardian.objects.filter(user_guardian=client)
    client_guardian = list(
        client_guardian_queryset.values('id', 'email', 'first_name', 'middle_name', 'last_name', 'phone_number',
                                        'status'))

    for guardian in client_guardian:
        addresses = ClientGuardianAddress.objects.filter(guardian__id=guardian['id']).first()

        # guardian_addresses = list()
        # for address in addresses:
        #     guardian_addresses.append(model_to_dict(address))
        if addresses:
            guardian['guardian_addresses'] = model_to_dict(addresses, exclude=['guardian'])

    addresses = list()
    for address in client.client_address.all():
        addresses.append(model_to_dict(address))

    service = []
    for client_service in client.client_service.all():
        service.append(model_to_dict(client_service))
    for _service in service:
        get_service = ProviderServices.objects.filter(id=_service['service']).all().values()
        _service['service'] = get_service[0]

    insurance = []
    for insurances in client.client_insurance.all():
        insurance.append(model_to_dict(insurances))

    authorization = []
    for authorize in client.client_authorization.all():
        authorization.append({
            'id': authorize.id,
            'authorization_number': authorize.authorization_number,
            'validity': authorize.validity,
            'number_of_uses': authorize.number_of_uses,
            'is_active': authorize.is_active,
            'reminding_before_expire': authorize.reminding_before_expire
        })

    user_id = client.id
    date_of_birth = client.date_of_birth
    date_started = client.date_started
    client = model_to_dict(client,
                           exclude=['password'])
    client['id'] = user_id
    client['date_started'] = date_started.timestamp() if date_started else None
    client['date_of_birth'] = date_of_birth.timestamp() if client['date_of_birth'] else None
    client['client_guardian'] = client_guardian
    client['client_address'] = addresses
    client['client_service'] = service
    client['client_insurance'] = insurance
    client['client_authorization'] = authorization
    get_avatar = generate_assigned_url(client['avatar']) if client['avatar'] else ''
    if get_avatar:
        client['avatar'] = get_avatar
    else:
        client['avatar'] = ''

    return Response(client, status=status.HTTP_200_OK)


@api_view(('GET', 'PATCH', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsClient])
@transaction.atomic
def client_id_api(request, client_id):
    try:
        client = get_object_or_404(Client, id=client_id)
        if request.method == 'PATCH':
            data = request.data
            if 'date_of_birth' in data and data['date_of_birth'] is not None:
                data['date_of_birth'] = datetime.fromtimestamp(int(data['date_of_birth']))

            if 'date_started' in data and data['date_started'] is not None:
                data['date_started'] = datetime.fromtimestamp(int(data['date_started']))

            if 'email' in data:
                email = data['email']
                if User.objects.filter(email=email).count() > 0:
                    raise ValidationError("Email already exist")
            if 'phone_number' in data:
                phone_number = data.get('phone_number')

                try:
                    parsed_phone_number = phonenumbers.parse(phone_number, "US")

                    # Check if the parsed phone number is valid
                    if phonenumbers.is_valid_number(parsed_phone_number):
                        print("Phone number is valid:",
                              phonenumbers.format_number(parsed_phone_number, phonenumbers.PhoneNumberFormat.E164))
                    else:
                        raise ValidationError("Phone number is invalid")
                except phonenumbers.phonenumberutil.NumberParseException:
                    raise ValidationError("Invalid phone number format")
            client_guardian = None
            insurance = None
            client_addresses = None
            authorization_tracking = None
            client_services = None
            if 'addresses' in data:
                client_addresses = data.pop('addresses')
            if 'client_guardian' in data:
                client_guardian = data.pop('client_guardian')

            if 'client_insurance' in data:
                insurance = data.pop('client_insurance')

            if 'authorization_tracking' in data:
                authorization_tracking = data.pop('authorization_tracking')

            if 'services' in data:
                client_services = data.pop('services')

            for field_name, field_value in data.items():
                if hasattr(Client, field_name):
                    setattr(client, field_name, field_value)

            client.save()
            is_minor = client.is_minor
            if 'is_minor' in data:
                is_minor = data.get('is_minor', client.is_minor)
                if is_minor is False:
                    ClientGuardian.objects.filter(user_guardian=client).delete()

            if client_guardian and is_minor:
                ClientGuardian.objects.filter(user_guardian=client).delete()
                save_client_guardian(client, client_guardian)

            if insurance:
                card = None
                try:
                    client_insurance = ClientInsurance.objects.get(client=client)
                    card = client_insurance.card
                    client_insurance.delete()
                except ClientInsurance.DoesNotExist:
                    pass

                update_client_insurance(client, card, insurance)

            if client_addresses:
                ClientAddress.objects.filter(client=client).delete()
                save_client_addresses(client, client_addresses)

            if authorization_tracking:
                ClientAuthorizationTracking.objects.filter(client=client).delete()
                save_client_authorization_tracking(client, authorization_tracking)

            if client_services:
                ClientServices.objects.filter(client=client).delete()
                for client_service in client_services:
                    provider_service = ProviderServices.objects.get(id=client_service['service'])
                    if 'fees' in client_service:
                        fees = client_service['fees']
                    else:
                        fees = provider_service.fee
                    is_default = client_service['is_default']
                    ClientServices.objects.create(client=client, service=provider_service,
                                                  is_default=is_default, fees=fees)

            client_guardian_queryset = ClientGuardian.objects.filter(user_guardian=client)
            client_guardian = list(
                client_guardian_queryset.values('id', 'email', 'first_name', 'middle_name', 'last_name', 'phone_number',
                                                'status'))

            for guardian in client_guardian:
                addresses = ClientGuardianAddress.objects.filter(guardian__id=guardian['id']).first()

                # guardian_addresses = list()
                # for address in addresses:
                #     guardian_addresses.append(model_to_dict(address))
                if addresses:
                    guardian['guardian_addresses'] = model_to_dict(addresses, exclude=['guardian'])
                else:
                    guardian['guardian_addresses'] = {}

            addresses = list()
            for address in client.client_address.all():
                addresses.append(model_to_dict(address))

            service = []
            for client_service in client.client_service.all():
                service.append(model_to_dict(client_service))
            for _service in service:
                get_service = ProviderServices.objects.filter(id=_service['service']).all().values()
                _service['service'] = get_service[0]

            insurance = []
            for insurances in client.client_insurance.all():
                insurance.append(model_to_dict(insurances))

            authorization = []
            for authorize in client.client_authorization.all():
                authorization.append({
                    'id': authorize.id,
                    'authorization_number': authorize.authorization_number,
                    'validity': authorize.validity,
                    'number_of_uses': authorize.number_of_uses,
                    'is_active': authorize.is_active,
                    'reminding_before_expire': authorize.reminding_before_expire
                })

            user_id = client.id
            date_of_birth = client.date_of_birth
            date_started = client.date_started
            client = model_to_dict(client,
                                   exclude=['password'])
            client['id'] = user_id
            client['date_started'] = date_started.timestamp() if date_started else None
            client['date_of_birth'] = date_of_birth.timestamp() if client['date_of_birth'] else None
            client['client_guardian'] = client_guardian
            client['client_address'] = addresses
            client['client_service'] = service
            client['client_insurance'] = insurance
            client['client_authorization'] = authorization
            get_avatar = generate_assigned_url(client['avatar']) if client['avatar'] else ''
            if get_avatar:
                client['avatar'] = get_avatar
            else:
                client['avatar'] = ''
            return Response(client, status=status.HTTP_200_OK)

        if request.method == 'GET':
            client_guardian_queryset = ClientGuardian.objects.filter(user_guardian=client)
            client_guardian = list(
                client_guardian_queryset.values('id', 'email', 'first_name', 'middle_name', 'last_name', 'phone_number',
                                                'status'))

            for guardian in client_guardian:
                addresses = ClientGuardianAddress.objects.filter(guardian__id=guardian['id']).first()

                # guardian_addresses = list()
                # for address in addresses:
                #     guardian_addresses.append(model_to_dict(address))
                if addresses:
                    guardian['guardian_addresses'] = model_to_dict(addresses, exclude=['guardian'])
                else:
                    guardian['guardian_addresses'] = {}

            addresses = list()
            for address in client.client_address.all():
                addresses.append(model_to_dict(address))

            service = []
            for client_service in client.client_service.all():
                service.append(model_to_dict(client_service))
            for _service in service:
                get_service = ProviderServices.objects.filter(id=_service['service']).all().values()
                _service['service'] = get_service[0]

            insurance = []
            for insurances in client.client_insurance.all():
                _insurance_obj = model_to_dict(insurances)
                if _insurance_obj['card']:
                    _insurance_obj['card'] = generate_assigned_url(_insurance_obj['card'])
                insurance.append(_insurance_obj)

            authorization = []
            for authorize in client.client_authorization.all():
                authorization.append({
                    'id': authorize.id,
                    'authorization_number': authorize.authorization_number,
                    'validity': authorize.validity,
                    'number_of_uses': authorize.number_of_uses,
                    'is_active': authorize.is_active,
                    'reminding_before_expire': authorize.reminding_before_expire
                })

            user_id = client.id
            date_of_birth = client.date_of_birth
            date_started = client.date_started
            client = model_to_dict(client,
                                   exclude=['password'])
            client['id'] = user_id
            client['date_started'] = date_started.timestamp() if date_started else None
            client['date_of_birth'] = date_of_birth.timestamp() if client['date_of_birth'] else None
            client['client_guardian'] = client_guardian
            client['client_address'] = addresses
            client['client_service'] = service
            client['client_insurance'] = insurance
            client['client_authorization'] = authorization
            get_avatar = generate_assigned_url(client['avatar']) if client['avatar'] else ''
            if get_avatar:
                client['avatar'] = get_avatar
            else:
                client['avatar'] = ''

            return Response(client, status=status.HTTP_200_OK)

        if request.method == 'DELETE':
            client.delete()
            return Response({"msg": "Client has been deleted"}, status=status.HTTP_204_NO_CONTENT)
    except ValidationError as e:
        # Extract specific error message from ValidationError
        error_message = str(e).split("'")[1].split("'")[0]  # Get first message part
        return Response({
            'success': False,
            'msg': error_message  # Return specific validation error message
        }, status=status.HTTP_400_BAD_REQUEST)

    except Exception as e:
        # Handle other unexpected exceptions
        return Response({
            'success': False,
            'message': 'Something went wrong. Please try again later.'  # Generic error message
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def get_client_details(request):
    queryset = Client.objects.all()
    current_datetime = datetime.now()
    get_status = request.GET.get('status')
    if get_status:
        queryset = queryset.filter(status=int(get_status))

    start_date_str = request.GET.get('start_date')
    end_date_str = request.GET.get('end_date')
    if start_date_str and end_date_str:
        start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
        end_date = datetime.strptime(end_date_str, '%Y-%m-%d')

        queryset = queryset.filter(date_started__range=(start_date, end_date))

    offset = int(request.GET.get('offset', 0))
    limit = int(request.GET.get('limit', 10))
    count = queryset.count()
    queryset = queryset[offset: offset + limit]

    # previous_appointment_subquery = Appointment.objects.filter(
    #     clients=OuterRef('pk'),
    #     start_date__date__lte=current_datetime.date()
    # ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by(
    #     '-start_date')
    #
    # previous_appointment_subquery = previous_appointment_subquery.exclude(
    #     start_date__date=current_datetime.date(),
    #     start_time__gt=current_datetime.time()
    # ). \
    #                                     values('start_date')[:1]
    #
    # queryset = queryset.annotate(
    #     latest_appointment=Subquery(previous_appointment_subquery)
    # )

    # if queryset:
    #     previous_appointment_subquery = Booking.objects.filter(
    #         clients=OuterRef('pk'),
    #         start_date__date__lte=current_datetime.date()
    #     ).exclude(status=BookingStatusChoices.CANCELED.value).order_by(
    #         '-start_date')
    #
    #     previous_appointment_subquery = previous_appointment_subquery.exclude(
    #         start_date__date=current_datetime.date(),
    #         start_time__gt=current_datetime.time()
    #     ). \
    #                                         values('start_date')[:1]
    #
    #     queryset = queryset.annotate(
    #         latest_appointment=Subquery(previous_appointment_subquery)
    #     )

    # client_subquery = ClientAddress.objects.filter(client=OuterRef('id')).order_by('id').values('address',
    #                                                                                             'city', 'zipcode',
    #                                                                                             'state')[
    #                   :1]
    # queryset = queryset.annotate(
    #     first_address_address=Subquery(client_subquery.values('address')),
    #     first_address_city=Subquery(client_subquery.values('city')),
    #     first_address_zipcode=Subquery(client_subquery.values('zipcode')),
    #     first_address_state=Subquery(client_subquery.values('state'))
    # )
    # queryset = queryset.values('id',
    #                            'first_name',
    #                            'middle_name',
    #                            'last_name',
    #                            'status',
    #                            'phone_number',
    #                            'email',
    #                            'first_address_address',
    #                            'first_address_city',
    #                            'first_address_zipcode',
    #                            'first_address_state',
    #                            'date_started',
    #                            'date_of_birth',
    #                            'relationship_status',
    #                            'employment_status',
    #                            'preferred_language',
    #                            'gender',
    #                            'race',
    #                            'gender_identity',
    #                            )

    response_data = []
    for client in queryset:
        data = {
            'id': client.id,
            'first_name': client.first_name,
            'middle_name': client.middle_name,
            'last_name': client.last_name,
            'status': client.status,
            'phone_number': client.phone_number,
            'email': client.email,
            'date_started': client.date_started if client.date_started else '',
            'date_of_birth': client.date_of_birth if client.date_of_birth else '',
            'relationship_status': client.relationship_status,
            'employment_status': client.employment_status,
            'preferred_language': client.preferred_language,
            'gender': client.gender,
            'race': client.race,
            'gender_identity': client.gender_identity,
            'first_address_address': '',
            'first_address_city': '',
            'first_address_zipcode': '',
            'first_address_state': '',
            'latest_appointment': None
        }
        addresses = ClientAddress.objects.filter(client=client.id).order_by('id').first()
        if addresses:
            data['first_address_address'] = addresses.address
            data['first_address_city'] = addresses.city
            data['first_address_state'] = addresses.state
            data['first_address_zipcode'] = addresses.zipcode

        appointments = Appointment.objects.filter(
            Q(clients=client.id, start_date__date__lte=current_datetime.date()) |
            Q(booking__start_date__date__lte=current_datetime.date(), clients=client.id)
        ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by('-start_date').values('start_date',
                                                                                                  'booking__start_date').first()
        if appointments:
            if appointments['start_date']:
                data['latest_appointment'] = appointments.get('start_date')
            elif appointments['booking__start_date']:
                data['latest_appointment'] = appointments.get('booking__start_date')

        response_data.append(data)

    return Response({'offset': offset, 'limit': limit, 'count': count, 'result': response_data})


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsClient])
@transaction.atomic
def my_provider_api(request):
    try:
        user = request.user
        client = Client.objects.get(id=user.id)
        with schema_context("public"):
            provider = Provider.objects.filter(id=client.user_provider).first()
            if provider:
                return Response({'id': provider.id, 'first_name': provider.first_name, 'last_name': provider.last_name,
                                 'middle_name': provider.middle_name, 'email': provider.email,
                                 'avatar': provider.avatar.url if provider.avatar else None,
                                 'phone_number': provider.phone_number,
                                 'provider_license': ProviderLicense.objects.filter(provider=provider).all().values(),
                                 'website_link': provider.website_link})
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to get provider data because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(('PATCH',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsClient])
@transaction.atomic
def user_insurance_image_change_api(request, user_pk):
    try:
        if not request.data.get('image'):
            return Response({
                'success': False,
                'message': 'Image is required'
            }, status=status.HTTP_400_BAD_REQUEST)
        get_client = Client.objects.filter(id=user_pk).first()
        if not get_client:
            return Response({
                'success': False,
                'message': f'Failed to upload image because client does not exist'
            }, status=status.HTTP_400_BAD_REQUEST)
        get_client_insurance = ClientInsurance.objects.filter(client=get_client).first()
        if not get_client_insurance:
            return Response({
                'success': False,
                'message': f'Failed to upload image because client insurance does not exist'
            }, status=status.HTTP_400_BAD_REQUEST)
        get_image = request.FILES.getlist('image')[0]
        get_image_name = get_image.name
        image_path = upload_file(get_image, get_image_name)
        get_client_insurance.card = get_image_name
        get_client_insurance.save()
        return Response(
            {'msg': 'Provider image has been updated'},
            status=status.HTTP_200_OK
        )
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to upload image because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def client_details(request):
    user = request.user
    client_id = request.GET.get('client_id', None)
    if client_id:
        client = Client.objects.filter(id=client_id).first()
        if client:
            thread = Thread(target=client_details_thread, args=[user, client])
            thread.start()
            return Response({'msg': 'Email Sent'}, status=status.HTTP_200_OK)
        else:
            return Response({'msg': 'Client Not Found'}, status=status.HTTP_204_NO_CONTENT)

    else:
        return Response({'msg': 'Invalid Request'}, status=status.HTTP_400_BAD_REQUEST)
